package darknet

import (
	"sync"
	"time"

	"gitee.com/dark.H/dark"
)

type Pool struct {
	running    chan any
	Output     chan any
	after      func(result any)
	before     func()
	RunningNum int
	runid      int64
	lock       sync.RWMutex
	Num        int
	Fin        bool
	Log        bool
	asyncAfter func(i any)
}

func NewPool(i int) *Pool {
	return &Pool{
		Num:        i,
		running:    make(chan any, i),
		Output:     make(chan any, i),
		asyncAfter: func(i any) {},
		Log:        true,
	}

}

func (pool *Pool) Async(anycall func(no int64) any) *Pool {

LAP:
	for {

		select {
		case pool.running <- 1:
			break LAP
		default:
			time.Sleep(300 * time.Millisecond)
		}
	}

	pool.lock.Lock()
	pool.RunningNum += 1
	pool.runid += 1
	pool.lock.Unlock()

	go func(e chan any, id int64) {
		r := anycall(id)
		// fmt.Println("Runn:", len(e), e)
		<-e
		pool.Output <- r
		pool.lock.Lock()
		pool.RunningNum -= 1
		pool.lock.Unlock()
	}(pool.running, pool.runid)
	return pool
}

func (pool *Pool) NoOutput() *Pool {
	go pool.AsyncOutput(func(result any) {})
	return pool
}

func (pool *Pool) Stop() {
	pool.Fin = true
}

func (pool *Pool) AsyncOutput(asyncAfter func(result any)) {
	pool.asyncAfter = asyncAfter
	inter := time.NewTicker(3 * time.Second)
ALL:
	for {
		select {
		case result := <-pool.Output:
			pool.asyncAfter(result)
		case <-inter.C:
			if pool.Log {
				dark.Str("Done: %d Running: %d Output: %d \r").F(pool.runid, pool.RunningNum, len(pool.Output)).Color("g", "B").Print()
			}
		default:
			time.Sleep(300 * time.Millisecond)
			if pool.Fin {
				break ALL
			}

		}
	}
}

func (pool *Pool) WaitStop() {
	time.Sleep(1 * time.Second)
	for {
		time.Sleep(100 * time.Millisecond)
		if pool.RunningNum == 0 {
			// fmt.Println("oo", len(pool.Output))
			if len(pool.Output) == 0 {
				break

			}
		}

		// fmt.Println("ff", pool.RunningNum)

	}
	pool.Fin = true

}

func (pool *Pool) Map(args dark.List[any], call func(no int64, i any) any, after ...func(i any)) *Pool {
	if after == nil {
		pool = pool.NoOutput()
	} else {
		go pool.AsyncOutput(after[0])
	}
	for _, i := range args {
		func(ii any) {
			pool.Async(func(no int64) any {
				return call(no, ii)
			})
		}(i)
	}
	pool.WaitStop()
	return pool
}
